package optimizacion;

import java.util.ArrayList;

import auxiliares.ParametrosSalida;
import auxiliares.PosicionesTableros;
import tablero.Espacio;
import tablero.Posicion;
import tablero.Tablero;
import tablero.Tablero.Estado;


public class Cromosoma {
	public Arbol genotipo; 
	
    public double aptitud;
    public double aptitudRev;
    public double aptitudEscaladoLineal;    
    public double puntuacion;
    public double puntuacionAcumulada;
    public int N;
    public int NUM_PARTIDAS;
    public int PENALIZACION;
    
    public int numPartidasGanadas;
    
    public int prof_max;
    public int tipoInicialializacion;
    public ArrayList<PosicionesTableros> tableroGanado;
   
    public Cromosoma(int n, int numPartidas, int penalizacion, int prof_max, int tipoInicialializacion){
    	genotipo = new Arbol(prof_max);
    	genotipo.tipoInicialializacion = tipoInicialializacion;
    	
    	aptitud = 0;
    	aptitudRev = 0;
    	aptitudEscaladoLineal = 0;
    	puntuacion = 0;
    	puntuacionAcumulada = 0;
    	
    	numPartidasGanadas = 0;
    	
    	N = n;
    	NUM_PARTIDAS = numPartidas;
    	PENALIZACION = penalizacion;
    	
    	this.prof_max = prof_max;
    	this.tipoInicialializacion = tipoInicialializacion;
    	
    }
   
    /**
     * Inicializa el genotipo
     */
	public void inicializar(){
		genotipo.creaArbol(0);
	}
	

	/**
	 * Funcion de adaptacion que calcula la aptitud
	 * @return
	 */
  	public double adaptacion(){
  		
  		int depurar = 0;
  		
  		Espacio espacio = new Espacio(N);
  		int adaptacion = 0, valorEvalua = 0;
  		int tiempo,tiempo_anterior;
  		int distancia, nuevaDistancia;
  		boolean[] termina = null;		  		
  		ParametrosSalida param;
  		numPartidasGanadas = 0;
  		
  		for (int np = 0; np < NUM_PARTIDAS; np++){
  			espacio.generarPartida();
  			//espacio.generarPartida4000();
  			
  			// copio los valores iniciales
  			if (numPartidasGanadas == 0){  				
  				tableroGanado = new ArrayList<PosicionesTableros>();
  				tableroGanado.add(new PosicionesTableros(espacio.defensor, espacio.alien, espacio.misil, espacio.hayMisil, ""));
  			}
  			
  			tiempo = 0;
  			distancia = 0;
  			valorEvalua = 0;
  			termina = espacio.terminacion();
  			param= new ParametrosSalida(distancia,tiempo);
  			
  			while (!termina[0]){
  				
  				tiempo_anterior = tiempo;
  				valorEvalua += evaluar(genotipo, espacio, param, true);
  				
  				tiempo = param.getTiempo();
  				distancia = param.getDistancia();

  				if (tiempo_anterior == tiempo){
  					tiempo++;  					
  					
  					nuevaDistancia = espacio.avanzarTiempo();
  					distancia += nuevaDistancia;
  					
  					if (numPartidasGanadas == 0){  						
  						tableroGanado.add(new PosicionesTableros(espacio.defensor, espacio.alien, espacio.misil, espacio.hayMisil, ""));  						
  					}  
  				}
  				
  				termina = espacio.terminacion();
  			}
  			adaptacion += distancia;
  			
  			depurar += distancia + valorEvalua;
  		
  			if(!termina[1]){ // no gana usuario
  	  			adaptacion += PENALIZACION;
  	  			depurar += PENALIZACION ; // PUESTO AHORA
  			} else {
  				numPartidasGanadas++;
  			}
  			
  			
  		}
  		
  		if (adaptacion < 0) adaptacion = 0; // PARA CAPAR COSAS NEGATIVAS
  		return adaptacion;
  	}
 
  	public Arbol crearEjemplo1(){
  		Arbol a = new Arbol(4);
  		
  		a.setDato("PROGN3");
	  		a.HI = new Arbol(4);
	  		a.HI.dato = "EQ";
		  		a.HI.HI = new Arbol(4);
		  		a.HI.HI.dato = "IF";
			  		a.HI.HI.HI = new Arbol(4);
			  		a.HI.HI.HI.dato = "distX";
			  		a.HI.HI.HC = new Arbol(4);
			  		a.HI.HI.HC.dato = "derecha";	
			  		a.HI.HI.HD = new Arbol(4);
			  		a.HI.HI.HD.dato = "derecha";		  		
		  		a.HI.HD = new Arbol(4);
		  		a.HI.HD.dato = "PROGN3";
			  		a.HI.HD.HI = new Arbol(4);
			  		a.HI.HD.HI.dato = "izquierda";
			  		a.HI.HD.HC = new Arbol(4);
			  		a.HI.HD.HC.dato = "izquierda";
			  		a.HI.HD.HD = new Arbol(4);
			  		a.HI.HD.HD.dato = "distY";	
	  		a.HC = new Arbol(4);
	  		a.HC.dato = "PROGN3";
		  		a.HC.HI = new Arbol(4);
		  		a.HC.HI.dato = "distY";
		  		a.HC.HC = new Arbol(4);
		  		a.HC.HC.dato = "distX";	
		  		a.HC.HD = new Arbol(4);
		  		a.HC.HD.dato = "distX";
	  		a.HD = new Arbol(4);
	  		a.HD.dato = "PROGN3";
		  		a.HD.HI = new Arbol(4);
		  		a.HD.HI.dato = "fuego";
		  		a.HD.HC = new Arbol(4);
		  		a.HD.HC.dato = "PROGN2";		  		
			  		a.HD.HC.HI = new Arbol(4);
			  		a.HD.HC.HI.dato = "izquierda";	
			  		a.HD.HC.HD = new Arbol(4);
			  		a.HD.HC.HD.dato = "izquierda";		  		
		  		a.HD.HD = new Arbol(4);
		  		a.HD.HD.dato = "PROGN3";
			  		a.HD.HD.HI = new Arbol(4);
			  		a.HD.HD.HI.dato = "izquierda";
			  		a.HD.HD.HC = new Arbol(4);
			  		a.HD.HD.HC.dato = "derecha";
			  		a.HD.HD.HD = new Arbol(4);
			  		a.HD.HD.HD.dato = "izquierda";		
  		
  		return a;
  	}
  	
  	
  	public Arbol crearEjemplo2(){
  		Arbol a = new Arbol(4);
  		
  		a.setDato("EQ");
	  		a.HI = new Arbol(4);
	  		a.HI.dato = "IF";
		  		a.HI.HI = new Arbol(4);
		  		a.HI.HI.dato = "EQ";
			  		a.HI.HI.HI = new Arbol(4);
			  		a.HI.HI.HI.dato = "IF";
				  		a.HI.HI.HI.HI = new Arbol(4);
				  		a.HI.HI.HI.HI.dato = "distX";
				  		a.HI.HI.HI.HC = new Arbol(4);
				  		a.HI.HI.HI.HC.dato = "fuego";
				  		a.HI.HI.HI.HD = new Arbol(4);
				  		a.HI.HI.HI.HD.dato = "distY";				  		
			  		a.HI.HI.HD = new Arbol(4);
			  		a.HI.HI.HD.dato = "distX";		  		
		  		a.HI.HC = new Arbol(4);
		  		a.HI.HC.dato = "IF";
			  		a.HI.HC.HI = new Arbol(4);
			  		a.HI.HC.HI.dato = "fuego";
			  		a.HI.HC.HC = new Arbol(4);
			  		a.HI.HC.HC.dato = "fuego";
			  		a.HI.HC.HD = new Arbol(4);
			  		a.HI.HC.HD.dato = "derecha";		  		
		  		a.HI.HD = new Arbol(4);
		  		a.HI.HD.dato = "derecha";		  		
	  		a.HD = new Arbol(4);
	  		a.HD.dato = "izquierda";
  		
  		return a;
  	}
  	
	public Arbol crearEjemplo3(){
  		Arbol a = new Arbol(4);
  		
  		a.setDato("PROGN3");
	  		a.HI = new Arbol(4);
	  		a.HI.dato = "derecha";
	  		a.HC = new Arbol(4);
	  		a.HC.dato = "derecha";		
	  		a.HD = new Arbol(4);
	  		a.HD.dato = "disY";
  		
  		return a;
  	}
	
	public Arbol crearEjemplo4(){
		Arbol a = new Arbol(4);
  		
  		a.setDato("EQ");
	  		a.HI = new Arbol(4);
	  		a.HI.dato = "PROGN2";
		  		a.HI.HI = new Arbol(4);
		  		a.HI.HI.dato = "PROGN3";
		  			a.HI.HI.HI = new Arbol(4);
			  		a.HI.HI.HI.dato = "izquierda";
			  		a.HI.HI.HC = new Arbol(4);
			  		a.HI.HI.HC.dato = "izquierda";
			  		a.HI.HI.HD = new Arbol(4);
			  		a.HI.HI.HD.dato = "fuego";
		  		a.HI.HD = new Arbol(4);
		  		a.HI.HD.dato = "derecha";	  		
	  		a.HD = new Arbol(4);
	  		a.HD.dato = "EQ";
		  		a.HD.HI = new Arbol(4);
		  		a.HD.HI.dato = "distX";
		  		a.HD.HD = new Arbol(4);
		  		a.HD.HD.dato = "derecha";
  		
  		return a;
	}
	
	
	public Arbol crearEjemplo4000(){
		Arbol a = new Arbol(4);
  		
  		a.setDato("IF");
	  		a.HI = new Arbol(4);
	  		a.HI.dato = "fuego";
	  		a.HC = new Arbol(4);
	  		a.HC.dato = "PROGN2";	  		
		  		a.HC.HI = new Arbol(4);
		  		a.HC.HI.dato = "fuego";
		  		a.HC.HD = new Arbol(4);
		  		a.HC.HD.dato = "derecha";	  		
	  		a.HD = new Arbol(4);
	  		a.HD.dato = "IF";
		  		a.HD.HI = new Arbol(4);
		  		a.HD.HI.dato = "izquierda";
		  		a.HD.HC = new Arbol(4);
		  		a.HD.HC.dato = "derecha";
		  		a.HD.HD = new Arbol(4);
		  		a.HD.HD.dato = "distY";
  		
  		return a;
	}
	
	
  	/**
  	 * La funcion comprueba si la raiz del arbol que debe evaluar es una hoja o no, es decir, si corresponde a un 
  	 * operando o a un operador. Si es un operando, aplica al tablero y sus ocupantes las modificaciones que correspondan en funcion
  	 * del operando que se trate y termina. Si es un operador, hace la evaluacion de los subarboles que corresponda.
  	 * DIST_X y DIST_Y no suonen avance de tiempo.
  	 * @param arbol
  	 * @param espacio
  	 * @param distancia
  	 * @param tiempo
  	 */
	public int evaluar(Arbol arbol, Espacio espacio, ParametrosSalida param, boolean guardarTablero) {
    	int valor1 = 0, valor2 = 0, valor3 = 0;
		int nuevaDistancia = 0;
		
		if(arbol.esHoja()){ // operando
			if(arbol.dato.equals("fuego")){
				if(!espacio.hayMisil){
					espacio.hayMisil = true; // misil en la posicion superior al defensor
					espacio.misil = new Posicion(espacio.defensor.x, espacio.defensor.y-1);					
					espacio.tablero.setEstado(espacio.misil, Estado.misil);
				}
				param.sumarTiempo(1);				
				nuevaDistancia = espacio.avanzarTiempo();
				guardarTablero(espacio, "fuego", guardarTablero);
				param.sumarDistancia(nuevaDistancia);
				valor1 = 1;
			} else if(arbol.dato.equals("izquierda")){
				if(espacio.defensor.x > 0){
					espacio.tablero.setEstado(espacio.defensor, Estado.libre);
					espacio.defensor.x--;
					espacio.tablero.setEstado(espacio.defensor, Estado.defensor);
				}
				param.sumarTiempo(1);
				nuevaDistancia = espacio.avanzarTiempo();
				guardarTablero(espacio, "izquierda", guardarTablero);				
				param.sumarDistancia(nuevaDistancia);
				valor1 = 1;
			} else if(arbol.dato.equals("derecha")){
				if(espacio.defensor.x < N-1){
					espacio.tablero.setEstado(espacio.defensor, Estado.libre);
					espacio.defensor.x++;
					espacio.tablero.setEstado(espacio.defensor, Estado.defensor);					
				}
				param.sumarTiempo(1);				
				nuevaDistancia = espacio.avanzarTiempo();
				guardarTablero(espacio, "derecha", guardarTablero);				
				param.sumarDistancia(nuevaDistancia);
				valor1 = 1;
			} else if(arbol.dato.equals("distY")){//Revisado valor1 = espacio.alien.y;    
				valor1 = espacio.alien.y; //(N-1)-espacio.alien.y; estaba bien al principio, porque penaliza menos estar menos alejado				
				guardarTablero(espacio, "distY", guardarTablero);				
			} else if(arbol.dato.equals("distX")){ //Revisado
				valor1 = Math.abs(espacio.alien.x - espacio.defensor.x);
				if((espacio.seAlejanAlienDefensor())){
					valor1 = -valor1;
				}
				guardarTablero(espacio, "distX", guardarTablero);
			}
			
			return valor1;
			
		} else { // es operador
			if(arbol.dato.equals("IF")){
				valor1 = evaluar(arbol.HI, espacio, param, guardarTablero);
				if(!espacio.termina()){
					if(valor1 != 0){
						valor2 = evaluar(arbol.HC, espacio, param, guardarTablero);
					}else{
						valor2 = evaluar(arbol.HD, espacio, param, guardarTablero);
					}	
					return valor1 +valor2;
				} else {
					return valor1;
				}
			} else if(arbol.dato.equals("PROGN2")){
				valor1 = evaluar(arbol.HI, espacio, param, guardarTablero);
				if(!espacio.termina()){
					valor2 = evaluar(arbol.HD, espacio,param, guardarTablero);
					//return valor1 + valor2; 
				}
				return valor1 + valor2; // PUESTO AHORA
			} else if(arbol.dato.equals("PROGN3")){
				valor1 = evaluar(arbol.HI, espacio, param, guardarTablero);
				if(!espacio.termina()){
					valor2 = evaluar(arbol.HC, espacio, param, guardarTablero);
					if(!espacio.termina()){
						valor3 = evaluar(arbol.HD, espacio, param, guardarTablero);
						//return valor1 + valor2 + valor3;
					}
				}	
				return valor1 + valor2 + valor3; // PUESTO AHORA
			} else if(arbol.dato.equals("EQ")){
				valor1 = evaluar(arbol.HI, espacio, param, guardarTablero);
				if(!espacio.termina()){
					valor2 = evaluar(arbol.HD, espacio, param, guardarTablero);
					if(valor1 == valor2) return 1; 
					else return 0;
				}				
				
			}
			return 0;
		}
  	}


	private void guardarTablero(Espacio espacio, String accion, boolean guardarTablero) {
		if (guardarTablero && numPartidasGanadas == 0){					
			if (guardarTablero && numPartidasGanadas == 0){					
				tableroGanado.add(new PosicionesTableros(espacio.defensor, espacio.alien, espacio.misil, espacio.hayMisil, accion));					
			}					
		}
	}

	public double calcularAptitud() {   
    	aptitud = adaptacion();
    	
    	aptitudRev = aptitud;
    	aptitudEscaladoLineal = aptitud;
    	return aptitud;
    }
   
	    
    public double revisarAptitud(double cmax) {   
    	aptitudRev =  calcularAptitudRevisadaMinimizacion(cmax);
    	aptitudEscaladoLineal = aptitudRev;
    	return aptitudRev;
    }
	
    public double calcularAptitudRevisadaMinimizacion(double cmax) {
		aptitudRev =  Math.abs(cmax*1.05) - aptitud; // he puesto abs porque si son todos negativos, uno se quedaria negativo
    	return aptitudRev;
	}
    
	/**
	 * Calcula la aptitud con escalado lineal
	 * @param P
	 * @param media
	 * @param cmax
	 * @param cmin
	 * @return
	 */
	public double calcularAptitudEscaladoLineal(double P, double media, double cmax, double cmin) {   
		double a = ((P - 1) * media) / (cmax - media);
		
		if (a < 0) // // para evitar negativos y que de valores erroneos
			a = media / (media - cmin);
			
		a = a + 0.05; // para evitar 0 y que de valores erroneos
		
		double b = (1 - a) * media; 		
		
    	aptitudEscaladoLineal =  a * aptitudRev + b;
    	
    	//System.out.println("----- m: " + media + ", a:" + a + ", b: " + b+ ",a:" + aptitud +", aR: "  + aptitudRev + ", aEL: " + aptitudEscaladoLineal);
    	
    	return aptitudEscaladoLineal;
    } 

	
	/**
	 * Calcula la puntuacion de un cromosoma
	 * @param total
	 * @return
	 */
	public double calcularPuntuacion(double total){
		puntuacion = aptitudEscaladoLineal / total;
		return puntuacion;
	}

	/**
	 * Calcula la puntuacion acumulada de un cromosoma
	 * @param acumulado
	 * @return
	 */
	public double calcularPuntuacionAcumulada(double acumulado){
		puntuacionAcumulada = puntuacion + acumulado;
		return puntuacionAcumulada;
	}  
	
	public Cromosoma clone (){
    	Cromosoma crom = new Cromosoma(N, NUM_PARTIDAS, PENALIZACION, prof_max, tipoInicialializacion); 
    	
    	crom.aptitud = aptitud;
    	crom.aptitudRev = aptitudRev;
    	crom.aptitudEscaladoLineal = aptitudEscaladoLineal;
    	crom.puntuacion = puntuacion;
    	crom.puntuacionAcumulada = puntuacionAcumulada;
    	crom.numPartidasGanadas = numPartidasGanadas;
    	
    	if (tableroGanado != null){
    		crom.tableroGanado = new ArrayList<PosicionesTableros>();
        	for (int i = 0; i < tableroGanado.size(); i++)
        		crom.tableroGanado.add(tableroGanado.get(i).clone()); 	
        }
    	
    	
   	  	
    	crom.genotipo = genotipo.clone();
    	
    	return crom;
    }

	public void setGenotipo(Arbol genotipo) {
		this.genotipo = genotipo;
	}
	
	public void mostrarTablero(Tablero t){
		System.out.print("  ");
		for (int i = 0; i < N; i++)
			System.out.print(i);
		System.out.print("\n");
		
		for (int i = 0; i < N; i++){
			String fila = i + " ";
			for (int j = 0; j < N; j++){
				fila += t.estado(i,j);
			}
			System.out.println(fila);
		}	
	}
	
	public static void main(String[] args) {
		Cromosoma c = new Cromosoma(20, 50, 200, 4, 0);
		c.inicializar();	
		c.genotipo = c.crearEjemplo4000();
		c.calcularAptitud();
		
		Cromosoma c2 = c.clone();
		
		System.out.println("Aptitud: " + c.aptitud);
		System.out.println("GANADAS: " + c.numPartidasGanadas);
	}
}
